import type { Address } from 'viem';
import type {
  Fee,
  QuoteWarning,
  SwapQuoteParams,
  TransactionParams,
} from '../swap/types';
import type { Token } from '../token/types';
import type { Call } from '../transaction/types';

export type AddressOrETH = Address | 'ETH';

/**
 * Note: exported as public Type
 */
export type APIError = {
  /** The Error code */
  code: string;
  /** The Error long message */
  error: string;
  /** The Error short message */
  message: string;
};

/**
 * Note: exported as public Type
 */
export type TransactionError = APIError;

/**
 * Note: exported as public Type
 */
export type NFTError = APIError | TransactionError;

/**
 * Note: exported as public Type
 */
export type BuildPayTransactionParams = {
  /** The address of the wallet paying */
  address: Address;
  /** The ID of the Commerce Charge to be paid */
  chargeId?: string;
  /** The ID of the product being paid for */
  productId?: string;
};

/**
 * Note: exported as public Type
 */
export type BuildPayTransactionResponse = PayTransaction | APIError;

/**
 * Note: exported as public Type
 */
export type BuildSwapTransaction = {
  /** ERC20 approve transaction which allows token holders to authorize spending */
  approveTransaction?: TransactionParams;
  /** The fee for the swap */
  fee: Fee;
  /** The quote for the swap */
  quote: SwapQuoteParams;
  /** The object developers should pass into Wagmi's signTransaction */
  transaction: TransactionParams;
  /** The warning associated with the swap */
  warning?: QuoteWarning;
};

/**
 * Note: exported as public Type
 */
export type BuildSwapTransactionParams = GetSwapQuoteParams & {
  /** The address of the user */
  fromAddress: Address;
};

/**
 * Note: exported as public Type
 */
export type BuildSwapTransactionResponse = BuildSwapTransaction | APIError;

export type CreateProductChargeParams = {
  /** The address of the wallet paying */
  sender: Address;
  /** The ID of the product being paid for */
  productId: string;
};

export type GetAPIParamsForToken =
  | GetSwapQuoteParams
  | BuildSwapTransactionParams;

export type GetQuoteAPIParams = {
  /** The amount to be swapped */
  amount: string;
  /** The reference amount for the swap, 'to' is only supported with v2Enabled: false */
  amountReference?: 'to' | 'from';
  /** The source address or 'ETH' for Ethereum */
  from: AddressOrETH | '';
  /** The destination address or 'ETH' for Ethereum */
  to: AddressOrETH | '';
  /** Whether to use V2 of the API (default: false) */
  v2Enabled?: boolean;
  /** The slippage percentage for the swap */
  slippagePercentage?: string;
};

export type GetSwapAPIParams = GetQuoteAPIParams & {
  /** The address of the user */
  fromAddress: Address;
};

/**
 * Note: exported as public Type
 */
export type GetSwapQuoteParams = {
  /** The amount to be swapped */
  amount: string;
  /** The reference amount for the swap, 'to' is only supported with v2Enabled: false */
  amountReference?: 'to' | 'from';
  /** The source token for the swap */
  from: Token;
  /** Whether the amount is in decimals */
  isAmountInDecimals?: boolean;
  /** The slippage of the swap */
  maxSlippage?: string;
  /** The destination token for the swap */
  to: Token;
  /** Whether to use a DEX aggregator */
  useAggregator: boolean;
};

/**
 * Note: exported as public Type
 */
export type GetSwapQuoteResponse = SwapQuoteParams | APIError;

/**
 * Note: exported as public Type
 */
export type GetTokensOptions = {
  /** The maximum number of tokens to return (default: 50) */
  limit?: string;
  /** The page number to return (default: 1) */
  page?: string;
  /** A string to search for in the token name, symbol or address */
  search?: string;
};

/**
 * Note: exported as public Type
 */
export type GetTokensResponse = Token[] | APIError;

export type HydrateChargeAPIParams = {
  /** The address of the wallet paying */
  sender: Address;
  /** The ID of the Commerce Charge to be paid */
  chargeId: string;
};

export type PayTransaction = {
  /** The id of the Commerce Charge to be paid */
  id: string;
  /** Collection of fields used to make the contract call to the Payment contract */
  callData: {
    /** Timestamp of when the payment will expire and be unpayable */
    deadline: string;
    /** The amount of the processing fee in the recipient currency */
    feeAmount: string;
    /** The id of the prepared transaction */
    id: string;
    /** The address of the operator of the Payment contract */
    operator: Address;
    /** The prefix of the signature generated by Commerce */
    prefix: Address;
    /** The address funds will settle in */
    recipient: Address;
    /** The amount the recipient will get in the recipient currency */
    recipientAmount: string;
    /** The address of the currency being paid (always USDC) */
    recipientCurrency: Address;
    /** The wallet address of the payer */
    refundDestination: Address;
    /** The signature generated by the Payment contract operator, encoding the payment details */
    signature: Address;
  };
  /** Collection of metadata needed to make the contract call to the Payment Contract */
  metaData: {
    /** The chain this prepared transaction can be paid on */
    chainId: number;
    /** The address of the Payment contract */
    contractAddress: Address;
    /** The wallet address of the payer */
    sender: Address;
    /** The address of the currency being paid (always USDC) */
    settlementCurrencyAddress: Address;
  };
};

export type RawTransactionData = {
  /** The transaction data */
  data: string;
  /** The sender address */
  from: string;
  /** The gas limit */
  gas: string;
  /** The gas price */
  gasPrice: string;
  /** The recipient address */
  to: string;
  /** The value of the transaction */
  value: string;
};

export type SwapAPIParams = GetQuoteAPIParams | GetSwapAPIParams;

export type ContractType = 'ERC721' | 'ERC1155';

export type NFTPrice = {
  /** Amount in Currency */
  amount?: string;
  /** Currency */
  currency?: string;
  /** Amount in USD */
  amountUSD?: string;
};

/**
 * Note: exported as public Type
 */
export type GetTokenDetailsParams = {
  /** The address of the token contract */
  contractAddress: Address;
  /** The ID of the token */
  tokenId?: string;
};

export type TokenDetails = {
  /** The name of the token */
  name: string;
  /** The description of the token */
  description: string;
  /** The image URL of the token */
  imageUrl: string;
  /** The animation URL of the token */
  animationUrl: string;
  /** The MIME type of the token */
  mimeType: string;
  /** The address of the owner of the token */
  ownerAddress: Address;
  /** The last sold price of the token */
  lastSoldPrice: NFTPrice;
  /** ERC721, ERC1155 */
  contractType: ContractType;
};

/**
 * Note: exported as public Type
 */
export type GetTokenDetailsResponse = TokenDetails | APIError;

/**
 * Note: exported as public Type
 */
export type GetMintDetailsParams = {
  /** The address of the token contract */
  contractAddress: Address;
  /** The address of the user */
  takerAddress?: Address;
  /** The ID of the token (required for ERC1155) */
  tokenId?: string;
};

export type MintDetails = {
  /** The name of the NFT */
  name: string;
  /** The description of the NFT */
  description: string;
  /** The image URL of the NFT */
  imageUrl: string;
  /** The animation URL of the NFT */
  animationUrl: string;
  /** The MIME type of the NFT */
  mimeType: string;
  /** ERC721, ERC1155 */
  contractType: ContractType;
  /** The price of the NFT */
  price: NFTPrice;
  /** The mint fee of the NFT */
  mintFee: NFTPrice;
  /** The maximum number of mints per wallet */
  maxMintsPerWallet: number;
  /** Whether the user is eligible to mint */
  isEligibleToMint: boolean;
  /** The address of the creator of the NFT */
  creatorAddress: Address;
  /** The total number of tokens */
  totalTokens: string;
  /** The total number of owners of the NFT */
  totalOwners: string;
  /** The network the NFT is on */
  network: string;
};

/**
 * Note: exported as public Type
 */
export type GetMintDetailsResponse = MintDetails | APIError;

/**
 * Note: exported as public Type
 */
export type BuildMintTransactionParams = {
  /** The address of the token contract to mint */
  mintAddress: Address;
  /** The address of the user */
  takerAddress: Address;
  /** The ID of the token */
  tokenId?: string;
  /** The number of tokens to mint */
  quantity: number;
  /** The network the mint contract is on */
  network?: string;
};

type MintTransaction = {
  call_data: {
    /** The transaction data */
    data: Address;
    /** The recipient address */
    to: Address;
    /** The sender address */
    from: Address;
    /** The value of the transaction */
    value: string;
  };
};

/**
 * Note: exported as public Type
 */
export type BuildMintTransactionResponse = MintTransaction | APIError;

/**
 * Note: exported as public Type
 */
export type GetPortfoliosParams = {
  /** The addresses of the wallets to get the portfolio for */
  addresses: Address[] | null | undefined;
};

export type Portfolio = {
  /** The address of the wallet */
  address: Address;
  /** The balance of the wallet in USD */
  portfolioBalanceInUsd: number;
  /** The tokens in the wallet */
  tokenBalances: PortfolioTokenWithFiatValue[];
};

export type PortfolioTokenWithFiatValue = Token & {
  /** The crypto balance of the token in the wallet */
  cryptoBalance: number;
  /** The USD balance of the token in the wallet */
  fiatBalance: number;
};

/**
 * Note: exported as public Type
 */
export type GetPortfoliosResponse = {
  /** The portfolios for the provided addresses */
  portfolios: Portfolio[];
};

/**
 * Note: exported as public Type
 */
export type BuildSendTransactionParams = {
  /** The address of the recipient */
  recipientAddress: Address;
  /** The address of the token to transfer */
  tokenAddress: Address | null;
  /** The amount of the transfer */
  amount: bigint;
};

/**
 * Note: exported as public Type
 */
export type BuildSendTransactionResponse = Call | APIError;

export type PriceQuoteToken = Address | 'ETH';

/**
 * Note: exported as public Type
 */
export type GetPriceQuoteParams = {
  /** The token to get the price quote for */
  tokens: PriceQuoteToken[];
};

type PriceQuote = {
  /** The name of the token */
  name: string;
  /** The symbol of the token */
  symbol: string;
  /** The contract address of the token */
  contractAddress: Address | '';
  /** The price of the token */
  price: string;
  /** The timestamp of the price quote */
  timestamp: number;
};

/**
 * Note: exported as public Type
 */
export type GetPriceQuoteResponse =
  | {
      /** The array of price quotes for the tokens */
      priceQuotes: PriceQuote[];
    }
  | APIError;
